package com.tj.collection.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.api.Page;
import com.fqgj.common.api.Response;
import com.fqgj.common.utils.*;
import com.fqgj.log.factory.LogFactory;
import com.fqgj.log.interfaces.Log;
import com.qianli.cashmama.product.facade.result.ProductCategory;
import com.tj.collection.bean.AgentVo;
import com.tj.collection.bean.RedisKey;
import com.tj.collection.bean.SystemConstants;
import com.tj.collection.db.dao.*;
import com.tj.collection.db.entity.*;
import com.tj.collection.db.entity.business.AllocatedLanguagesVo;
import com.tj.collection.db.entity.business.ClDailyOrderCountEntity;
import com.tj.collection.db.entity.business.ClLanguageOrderCountEntity;
import com.tj.collection.enums.AdminNumberRuleEnum;
import com.tj.collection.enums.AllocationBatchEnum;
import com.tj.collection.enums.AllocationStatusEnum;
import com.tj.collection.enums.ReceiverStatusEnum;
import com.tj.collection.integration.ProductIntegrationService;
import com.tj.collection.utils.ConfigUtil;
import com.tj.collection.utils.DispatchUtils;
import com.tj.collection.utils.SerialNumberUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * User: sm
 * Date: 2019-09-03
 * Time: 09:45
 * <p>
 * 分单的任务
 */
@Service
public class AllocationJob implements SimpleJob {

    private static final Log LOGGER = LogFactory.getLog(AllocationJob.class);

    @Autowired
    private ProductIntegrationService productIntegrationService;

    @Autowired
    private ClStageDao stageDao;

    @Autowired
    private ClAgentsDao agentsDao;

    @Autowired
    private ClReceiverDao receiverDao;

    @Autowired
    private ClAllocationBatchDao allocationBatchDao;

    @Autowired
    private ClAllocationDetailDao allocationDetailDao;

    @Autowired
    private ConfigUtil configUtil;

    @Autowired
    private RedisClient redisClient;

    @Override
    public void execute(ShardingContext shardingContext) {

        LOGGER.info("开始进入分单");

        if (!configUtil.isAllocateSwitchOpen()) {
            LOGGER.info("开始进入分单,开关未开启");
            return;
        }

        this.run();
    }

    /**
     * 执行任务
     */
    public void run() {
        if (redisClient.get(RedisKey.COLLECTION_ALLOCATION_DAILY_KEY, DateUtil.getDate(new Date())) == null) {
            LOGGER.info("开始进入分单,等待逾期天数更新");
            return;
        }

        if (true) {
            this.orderAllocation();
            LOGGER.info("分单结束222");
        } else {
            this.runOld();
            LOGGER.info("分单结束");
        }
    }

    @Deprecated
    public void runOld() {
        //商户之间的数据进行隔离
        Response<List<ProductCategory>> allActiveProductCategory = productIntegrationService.getAllActiveProductCategory();
        if (allActiveProductCategory == null || CollectionUtils.isEmpty(allActiveProductCategory.getData())) {
            LOGGER.error("== 未找到活跃的商户信息 ==");
            return;
        }

        Set<String> activeMerchantList = allActiveProductCategory.getData().parallelStream().map(ProductCategory::getMerchantCode).collect(Collectors.toSet());
        LOGGER.info("== 活跃的商户列表 ==，activeMerchantList:{}", JSON.toJSONString(activeMerchantList));

        activeMerchantList.forEach(merchantCode -> {
            if (StringUtils.isEmpty(merchantCode)) {
                LOGGER.error("== merchantCode为空 ==");
                return;
            }
            Map<String, List<AgentVo>> agentsOfCurrentMerchant = findAgentsOfCurrentMerchant(merchantCode);
            if (CollectionUtils.isEmpty(agentsOfCurrentMerchant) || CollectionUtils.isEmpty(agentsOfCurrentMerchant.values())) {
                LOGGER.info("== 没有找到商户对应的催收人员 ==，merchantCode:{}", merchantCode);
                return;
            }
            LOGGER.info("== 找到商户对应的催收人员 ==，merchantCode:{},size:{},agentsOfCurrentMerchant:{}", merchantCode, agentsOfCurrentMerchant.size(), JSON.toJSONString(agentsOfCurrentMerchant));

            Map<String, List<ClReceiverEntity>> ordersToBeAllocateOfCurrentMerchant = findOrdersToBeAllocateOfCurrentStageCodeAndMerchant(merchantCode);
            if (CollectionUtils.isEmpty(ordersToBeAllocateOfCurrentMerchant) || CollectionUtils.isEmpty(ordersToBeAllocateOfCurrentMerchant.values())) {
                LOGGER.info("== 没有找到商户对应的待分配的订单 ==，merchantCode:{}", merchantCode);
                return;
            }
            LOGGER.info("== 找到商户对应的待分配的订单 ==，merchantCode:{},stageCodeSize:{},orderSize:{}", merchantCode, ordersToBeAllocateOfCurrentMerchant.size(), ordersToBeAllocateOfCurrentMerchant.values().size());

            ordersToBeAllocateOfCurrentMerchant.forEach((stageCode, orderList) -> {

                LOGGER.info("== 找到商户对应的待分配的订单 ordersToBeAllocateOfCurrentMerchant ==，merchantCode:{},stageCode:{},size:{}", merchantCode, stageCode, orderList.size());
                if (CollectionUtils.isEmpty(orderList)) {
                    return;
                }

                Map<String, List<ClReceiverEntity>> languageAndOrderListMap = new HashMap<>();
                orderList.forEach(o -> {
                    String userLanguage = o.getUserLanguage();
                    List<ClReceiverEntity> receiverEntityList = new ArrayList<>();
                    if (languageAndOrderListMap.containsKey(userLanguage)) {
                        receiverEntityList = languageAndOrderListMap.get(userLanguage);
                    }

                    receiverEntityList.add(o);
                    languageAndOrderListMap.put(userLanguage, receiverEntityList);

                    LOGGER.info("== 找到商户对应的待分配的订单 languageAndOrderListMap ==，collectionNo:{},merchantCode:{},stageCode:{},userLanguage:{},size:{}", o.getCollectionNo(), merchantCode, stageCode, userLanguage, receiverEntityList.size());
                });


                Map<String, Integer> languageAndSizeMap = new HashMap<>();
                languageAndOrderListMap.forEach((language, orders) -> {
                    languageAndSizeMap.put(language, orders.size());
                    LOGGER.info("== 找到商户对应的待分配的订单 languageAndOrderListMap ==，merchantCode:{},stageCode:{},userLanguage:{},size:{}", merchantCode, stageCode, language, orders.size());
                });

                //分单逻辑，列出所有的当前的阶段下的催收人员
                List<AgentVo> agentVos2 = agentsOfCurrentMerchant.get(stageCode);

                if (CollectionUtils.isEmpty(agentVos2)) {
                    LOGGER.error("== 当前阶段没有催收 ==,stageCode:{}", stageCode);
                    return;
                }
                LOGGER.info("== 找到商户对应的待分配的订单 agentVos2 ==，merchantCode:{},stageCode:{},agentVos2:{}", merchantCode, stageCode, JSON.toJSONString(agentVos2));

                //根据当前催收已有的订单初始化当前的agent
                initAgentVos(stageCode, agentVos2);

                DispatchUtils.run(languageAndSizeMap, agentVos2);

                //获取分单之后的这批催收的人员分工
                List<AgentVo> agentVos = DispatchUtils.getAgentVos();
                LOGGER.info("== 调用分配算法完成 ==,stageCode:{}，agentVos:{}", stageCode, JSON.toJSONString(agentVos));
                agentVos.forEach(a -> {
                    LOGGER.info("stage:" + stageCode + ",name:" + a.getAgentId() + ",should:" + a.getMaxNum() + ",num:" + a.getOrderNum());
                });


                Map<String, List<AgentVo>> languageAndAgentMap = new HashMap<>();
                agentVos.forEach(a -> {
                    List<String> languageList = a.getLanguageList();
                    languageList.forEach(l -> {
                        List<AgentVo> agentVos1 = new ArrayList<>();
                        if (languageAndAgentMap.containsKey(l)) {
                            agentVos1 = languageAndAgentMap.get(l);
                        }

                        agentVos1.add(a);
                        languageAndAgentMap.put(l, agentVos1);
                    });

                });

                //开始分配具体的订单,保存的是每个adminId 当前阶段分配的订单
                Map<String, List<ClReceiverEntity>> dispatchResult = new HashMap<>();
                orderList.forEach(order -> {
                    String userLanguage = order.getUserLanguage();
                    List<AgentVo> tempAgents = languageAndAgentMap.get(userLanguage);
                    if (CollectionUtils.isEmpty(tempAgents)) {
                        LOGGER.error("== 当前语言没有催收 ==，collectionNo:{},stateCode:{},userLanguage:{}", order.getCollectionNo(), stageCode, userLanguage);
                        return;
                    }

                    for (AgentVo e : tempAgents) {
                        Integer max = e.getLanguageSupportNumMap().get(userLanguage);
                        Integer actural = e.getLanguageAllocatedNumMap().get(userLanguage);
                        if (actural == null) {
                            actural = 0;
                        }

                        if (actural < max) {
                            e.getLanguageAllocatedNumMap().put(userLanguage, actural + 1);
                            List<ClReceiverEntity> allocatedOrders = new ArrayList<>();
                            if (dispatchResult.containsKey(e.getAgentId())) {
                                allocatedOrders = dispatchResult.get(e.getAgentId());
                            }

                            allocatedOrders.add(order);
                            dispatchResult.put(e.getAgentId(), allocatedOrders);
                            break;
                        }
                    }

                });

                //找到adminId和催收的详细数据的关联关系，用以管理agentVo和订单列表的关系
                Map<String, AgentVo> adminIdAndAgentVoMap = new HashMap<>();
                agentsOfCurrentMerchant.values().forEach(l -> {
                    l.forEach(a -> {
                        if (!adminIdAndAgentVoMap.containsKey(a.getAgentId())) {
                            adminIdAndAgentVoMap.put(a.getAgentId(), a);
                        }
                    });
                });

                //处理dispatchResult
                dispatchResult.forEach((adminId, orders) -> {
                    AgentVo agentVo = adminIdAndAgentVoMap.get(adminId);
                    store(agentVo.getName(), agentVo.getAgentId(), orders, merchantCode, stageCode, "system");
                });
            });

        });
    }


    //获取当前用户已有的订单
    private void initAgentVos(String stageCode, List<AgentVo> agentVos) {
        if (CollectionUtils.isEmpty(agentVos)) {
            LOGGER.error("initAgentVos 分单发生错误 stageCode:{}", stageCode);
            return;
        }

        agentVos.forEach(agentVo -> {
            List<ClAllocationBatchEntity> batchEntities = allocationBatchDao.getActiveListByAdminIdAndStageCode(Integer.valueOf(agentVo.getAgentId()), stageCode);
            List<String> batchNos = batchEntities.parallelStream().map(ClAllocationBatchEntity::getBatchNo).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(batchNos)) {
                return;
            }
            List<AllocatedLanguagesVo> allocatedStatsOfLanguageByBatchNos = null;
            try {
                allocatedStatsOfLanguageByBatchNos = allocationBatchDao.getAllocatedStatsOfLanguageByBatchNos(batchNos);
                Integer totalNum = 0;
                Map<String, Integer> languageSupportNumMap = new HashMap<>();
                Map<String, Integer> languageAllocatedNumMap = new HashMap<>();
                for (AllocatedLanguagesVo allocatedLanguagesVo : allocatedStatsOfLanguageByBatchNos) {
                    totalNum = totalNum + allocatedLanguagesVo.getTotalNum();
                    languageSupportNumMap.put(allocatedLanguagesVo.getLanguage(), allocatedLanguagesVo.getTotalNum());
                    languageAllocatedNumMap.put(allocatedLanguagesVo.getLanguage(), allocatedLanguagesVo.getTotalNum());
                }

                agentVo.setOrderNum(totalNum)
                        .setLanguageAllocatedNumMap(languageAllocatedNumMap)
                        .setLanguageSupportNumMap(languageSupportNumMap)
                        .setLanguageList(allocatedStatsOfLanguageByBatchNos.parallelStream().map(AllocatedLanguagesVo::getLanguage).collect(Collectors.toList()))
                        .setLanguageNum(allocatedStatsOfLanguageByBatchNos.size());
            } catch (Exception e) {
                LOGGER.error("== initAgentVos error ==,stageCode:{},agentVos:{}", stageCode, JSON.toJSONString(agentVos));
            }
        });


    }

    //存储当前催收 以及对应的催收的单子
    public void store(String agentName, String agentId, List<ClReceiverEntity> receiverEntities, String merchantCode, String stageCode, String createBy) {
        //把这些订单按照创建时间再区分一次
        Map<Date, List<ClReceiverEntity>> repaymentDateAndOrderListMap = new HashMap<>();
        receiverEntities.forEach(r -> {
            //根据逾期天数 反推应还日期
            Date repaymentDate = DateUtil.addDate(DateUtil.getPatternDate(new Date(), "yyyy-MM-dd"), -1 * r.getOverdueDays());
            List<ClReceiverEntity> tempList = new ArrayList<>();
            if (repaymentDateAndOrderListMap.containsKey(repaymentDate)) {
                tempList = repaymentDateAndOrderListMap.get(repaymentDate);
            }

            tempList.add(r);
            repaymentDateAndOrderListMap.put(repaymentDate, tempList);
        });


        repaymentDateAndOrderListMap.forEach((repaymentDate, list) -> {
            //创建一个批次号
            String batchNo = SerialNumberUtil.getBizOrderNumber(AdminNumberRuleEnum.BATCH);

            //插入批次记录
            ClAllocationBatchEntity clAllocationBatchEntity = new ClAllocationBatchEntity()
                    .setBatchNo(batchNo)
                    .setAdminId(Integer.valueOf(agentId))
                    .setAgentName(agentName)
                    .setMerchantCode(merchantCode)
                    .setStageCode(stageCode)
                    .setCreateDt(new Date())
                    .setRepaymentDt(repaymentDate)
                    .setStatus(AllocationBatchEnum.INIT.getStatus())
                    .setIsStart(1)
                    .setCreatedBy(createBy);

            ClAllocationBatchEntity newClAllocationBatchEntity = allocationBatchDao.insert(clAllocationBatchEntity);

            list.forEach(r -> {
                String allocationNo = SerialNumberUtil.getBizOrderNumber(AdminNumberRuleEnum.ALLOCATION);
                ClAllocationDetailEntity clAllocationDetailEntity = new ClAllocationDetailEntity()
                        .setBatchNo(batchNo)
                        .setAllocationNo(allocationNo)
                        .setCollectionNo(r.getCollectionNo())
                        .setStatus(AllocationStatusEnum.INIT.getStatus())
                        .setAdminId(newClAllocationBatchEntity.getAdminId());

                allocationDetailDao.insert(clAllocationDetailEntity);

                r.setStatus(ReceiverStatusEnum.ALLOCATE_SUCCESS.getStatus());
                r.setBatchNo(batchNo);
                r.setLastAgentId(r.getCurrentAgentId());
                r.setCurrentAgentId(Integer.valueOf(agentId));
                receiverDao.updateByPrimaryKey(r);
            });

            //激活批次
            newClAllocationBatchEntity.setStatus(AllocationBatchEnum.SUCCESS.getStatus());
            allocationBatchDao.updateByPrimaryKey(newClAllocationBatchEntity);
        });

    }


    //找到等待分单的订单 阶段code 对应阶段下面的订单
    private Map<String, List<ClReceiverEntity>> findOrdersToBeAllocateOfCurrentStageCodeAndMerchant(String merchantCode) {

        Map<String, List<ClReceiverEntity>> res = new HashMap<>();
        Map<Integer, ClStageEntity> allStageOfCurrentMerchant = findAllStageOfCurrentMerchant(merchantCode);
        //按照日期一天天的分过去
        for (int i = 0; i < SystemConstants.MAX_COLLECTION_DAYS; i++) {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, 0 - i);
            Date time = calendar.getTime();
            Date date = TimeUtils.getDate("yyyy-MM-dd", time);

            Integer overdueDays = TimeUtils.diffDays(date, new Date());
            ClStageEntity stageEntity = allStageOfCurrentMerchant.get(overdueDays);
            if (stageEntity == null) {
                continue;
            }
            List<ClReceiverEntity> receiverEntities = new ArrayList<>();
            if (res.containsKey(stageEntity.getStageCode())) {
                receiverEntities = res.get(stageEntity.getStageCode());
            }
            List<ClReceiverEntity> waittingForAllocationList = receiverDao.selectListByCreateDtAndStatusAndMerchantCode(date, ReceiverStatusEnum.INIT.getStatus(), merchantCode);
            receiverEntities.addAll(waittingForAllocationList);

            res.put(stageEntity.getStageCode(), receiverEntities);
        }

        return res;
    }

    //批次撤单
    public void callBackAllCasesByStageCode(String stageCode) {

        List<ClAllocationBatchEntity> allActiveBatchByStageCode = allocationBatchDao.getAllActiveBatchByStageCode(stageCode);
        allActiveBatchByStageCode.forEach(batch -> {
            batch.setStatus(AllocationBatchEnum.END.getStatus());
            allocationBatchDao.updateByPrimaryKey(batch);
            allocationDetailDao.updateStatusByBatchNoAndStatus(batch.getBatchNo(), Arrays.asList(0, 1), AllocationStatusEnum.INVALID.getStatus());
            receiverDao.updateStatusByBatchNo(batch.getBatchNo(), AllocationStatusEnum.INIT.getStatus());
        });

    }


    //获取每个阶段下面对应的催收人员
    private Map<String, List<AgentVo>> findAgentsOfCurrentMerchant(String merchantCode) {
        List<ClAgentsEntity> agentsEntities = agentsDao.getActiveListByMerchantCode(merchantCode);

        //增加催收随机
        Collections.shuffle(agentsEntities);

        if (CollectionUtils.isEmpty(agentsEntities)) {
            return null;
        }

        Map<String, Set<AgentVo>> res = new HashMap<>();
        agentsEntities.forEach(agent -> {
            String stageCode = agent.getStageCode();
            Set<AgentVo> agentVos = new HashSet<>();
            if (res.containsKey(stageCode)) {
                agentVos = res.get(stageCode);
            }

            List<String> languagesList = getLanguageList(agent.getExtraAttribute());
            AgentVo agentVo = new AgentVo(agent.getAdminName(), agent.getAdminId().toString(), languagesList, agent.getMaxOrdersNum());
            agentVos.add(agentVo);

            res.put(stageCode, agentVos);

        });

        Map<String, List<AgentVo>> r = new HashMap<>();
        res.forEach((a, b) -> {
            List<AgentVo> agentVos = new ArrayList<>();
            agentVos.addAll(b);
            r.put(a, agentVos);
        });


        return r;
    }

    /**
     * 找到当前商户对应的阶段设置
     *
     * @param merchantCode
     * @return
     */
    private Map<Integer, ClStageEntity> findAllStageOfCurrentMerchant(String merchantCode) {
        List<ClStageEntity> stagesByMerchantCode = stageDao.getStagesByMerchantCode(merchantCode);
        if (CollectionUtils.isEmpty(stagesByMerchantCode)) {
            LOGGER.error("== 没有找到商户对应的催收stage ==,merchantCode:{}", merchantCode);
            return null;
        }

        Map<Integer, ClStageEntity> overdueDaysAndStageMap = new HashMap<>();
        for (int i = 0; i < 200; i++) {
            for (ClStageEntity s : stagesByMerchantCode) {
                if (s.getMaxOverdueDays() != null) {
                    if (s.getMinOverdueDays() <= i && s.getMaxOverdueDays() >= i) {
                        overdueDaysAndStageMap.put(i, s);
                    }
                } else {
                    if (s.getMinOverdueDays() <= i) {
                        overdueDaysAndStageMap.put(i, s);
                    }
                }
            }
        }

        return overdueDaysAndStageMap;
    }


    public static List<String> getLanguageList(String config) {
        List<String> languages = new ArrayList<>();
        JSONObject jsonObject = JSON.parseObject(config);

        String mapStr = jsonObject.getString("languageWeight");
        List<Map<String, String>> maps = (List) JSON.parse(mapStr);
        maps.forEach(m -> {
            languages.add(m.get("userLanguage"));
        });

        return languages;
    }

//    public static void main(String[] args) {
//        String a = "{\"languageWeight\":[{\"userLanguage\":\"Hindi\",\"weight\":1},{\"userLanguage\":\"Telugu\",\"weight\":1},{\"userLanguage\":\"Kannada\",\"weight\":1},{\"userLanguage\":\"Tamil\",\"weight\":1},{\"userLanguage\":\"Malayalam\",\"weight\":1}],\"demandNum\":99999}";
//
//        List<String>  languages = new ArrayList<>();
//        JSONObject jsonObject = JSON.parseObject(a);
//
//        String mapStr = jsonObject.getString("languageWeight");
//        List<Map<String, String>> maps = (List)JSON.parse(mapStr);
//        maps.forEach(m->{
//            languages.add( m.get("userLanguage"));
//        });
//
//
//        System.err.println(11);
//    }

    public static void main(String[] args) {
        String s1 = "119~_~40389~_~~_~M Abhishek~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"},{\"weight\":1,\"userLanguage\":\"Telugu\"}]}~_~1~_~100~_~80~_~1~_~0~_~2020-04-08 08:25:34~_~2019-12-19 18:38:16\n" +
                "126~_~40397~_~~_~K R Rahul~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Malayalam\"},{\"weight\":1,\"userLanguage\":\"Hindi\"}]}~_~1~_~100~_~80~_~1~_~0~_~2019-12-23 19:05:39~_~2019-12-23 19:05:39\n" +
                "215~_~40513~_~~_~Mohd Beebi~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"},{\"weight\":1,\"userLanguage\":\"Telugu\"}]}~_~1~_~120~_~100~_~1~_~0~_~2020-04-16 10:10:51~_~2020-02-11 10:58:24\n" +
                "216~_~40516~_~~_~Shaik Naziya Tabissum~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"}]}~_~1~_~120~_~100~_~1~_~0~_~2020-04-16 11:25:13~_~2020-02-12 18:38:55\n" +
                "236~_~40542~_~~_~Syed shukoor~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"}]}~_~1~_~120~_~100~_~1~_~0~_~2020-04-16 10:11:17~_~2020-02-24 18:39:06\n" +
                "242~_~40561~_~~_~AMBIKA SUTAR~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"},{\"weight\":1,\"userLanguage\":\"Kannada\"},{\"weight\":1,\"userLanguage\":\"Telugu\"}]}~_~1~_~120~_~100~_~1~_~0~_~2020-04-16 09:14:11~_~2020-03-01 11:19:51\n" +
                "281~_~40594~_~~_~B Nikhil~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Telugu\"},{\"weight\":1,\"userLanguage\":\"Hindi\"}]}~_~1~_~150~_~130~_~1~_~0~_~2020-04-16 10:22:08~_~2020-03-10 18:47:13\n" +
                "283~_~40596~_~~_~Sita Ajay~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"},{\"weight\":1,\"userLanguage\":\"Telugu\"}]}~_~1~_~120~_~100~_~1~_~0~_~2020-04-16 10:12:05~_~2020-03-10 19:10:51\n" +
                "290~_~40604~_~~_~Sokkanuru lohita rani~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"},{\"weight\":1,\"userLanguage\":\"Telugu\"}]}~_~1~_~150~_~120~_~1~_~0~_~2020-04-16 10:01:35~_~2020-03-11 18:32:15\n" +
                "292~_~40606~_~~_~Venkat~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Telugu\"},{\"weight\":1,\"userLanguage\":\"Hindi\"}]}~_~1~_~150~_~120~_~1~_~0~_~2020-04-16 09:59:05~_~2020-03-12 06:21:11\n" +
                "325~_~40627~_~~_~Rehman Approvals~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Hindi\"},{\"weight\":1,\"userLanguage\":\"Telugu\"}]}~_~1~_~100~_~99~_~1~_~0~_~2020-04-13 07:53:32~_~2020-03-16 14:49:11\n" +
//                "385~_~40663~_~~_~Pavan Tamil~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[{\"weight\":1,\"userLanguage\":\"Tamil\"}]}~_~1~_~300~_~250~_~1~_~0~_~2020-03-27 10:33:05~_~2020-03-27 10:18:05\n" +
                "445~_~40676~_~~_~Midhun CC~_~cashmamaS2~_~CASHMAMA~_~{\"languageWeight\":[]}~_~1~_~150~_~100~_~1~_~0~_~2020-04-16 12:04:03~_~2020-04-16 12:04:03";

        String[] s2 = s1.split("\n");
        List<ClAgentsItem> clAgentsItemList = new ArrayList<>();
        for (String s : s2) {
            String[] s3 = s.split("~_~");
            ClAgentsEntity clAgentsEntity = new ClAgentsEntity();
            clAgentsEntity.setAdminId(Integer.valueOf(s3[1]));
            clAgentsEntity.setNextAdminId(null);
            clAgentsEntity.setAdminName(s3[3]);
            clAgentsEntity.setType(Integer.valueOf(s3[7]));
            clAgentsEntity.setStageCode(s3[4]);
            clAgentsEntity.setMerchantCode(s3[5]);
            clAgentsEntity.setExtraAttribute(s3[6]);
            clAgentsEntity.setMaxOrdersNum(99999);
            clAgentsEntity.setMinOrdersNum(0);
            clAgentsEntity.setStatus(1);

            ClAgentsItem clAgentsItem = new ClAgentsItem();
            BeanUtils.copyProperties(clAgentsEntity, clAgentsItem);

            List<String> userLanguageList = new ArrayList<>();
            JSONObject j1 = JSON.parseObject(clAgentsEntity.getExtraAttribute());
            JSONArray j2 = j1.getJSONArray("languageWeight");
            for (int i = 0; i < j2.size(); i++) {
                String ss = j2.getJSONObject(i).getString("userLanguage");
                userLanguageList.add(ss);
            }
            clAgentsItem.setUserLanguageList(userLanguageList);
            clAgentsItemList.add(clAgentsItem);
        }
        System.out.println(JSON.toJSONString(clAgentsItemList));

        List<LanguageCount> languageCounts = new ArrayList<>();
        languageCounts.add(new LanguageCount().setUserLanguage("Hindi").setCount(1232));
        languageCounts.add(new LanguageCount().setUserLanguage("Telugu").setCount(395));
        languageCounts.add(new LanguageCount().setUserLanguage("Kannada").setCount(261));
        languageCounts.add(new LanguageCount().setUserLanguage("Tamil").setCount(182));
        languageCounts.add(new LanguageCount().setUserLanguage("Malayalam").setCount(64));

        new AllocationJob().dataMatching(languageCounts, clAgentsItemList);
    }


    /**
     * 分单思路：
     * 1 遍历商户
     * 2 遍历逾期天数
     * 3 针对X商户 逾期N天的订单， 计算每个语言的订单数
     * 3.1 获取当前商户当前步骤的催收员
     * 3.2 计算每个催收员 各门语言的订单需求量
     * 4 根据催收员的订单需求量 根据语言 捞取订单 创建批次、分配
     * 注意：分配过程中需要根据最大单量进行控制
     */
    public void orderAllocation() {
        Date today = DateUtil.getPatternDate(new Date(), "yyyy-MM-dd");

        //商户之间的数据进行隔离
        Response<List<ProductCategory>> allActiveProductCategory = productIntegrationService.getAllActiveProductCategory();
        if (allActiveProductCategory == null || CollectionUtils.isEmpty(allActiveProductCategory.getData())) {
            LOGGER.error("== 未找到活跃的商户信息 ==");
            return;
        }

        Set<String> activeMerchantList = allActiveProductCategory.getData().parallelStream().map(ProductCategory::getMerchantCode).collect(Collectors.toSet());
        LOGGER.info("活跃的商户列表,today:{},activeMerchantList:{}", DateUtil.getDate(today), JSON.toJSONString(activeMerchantList));

        for (String merchantCode : activeMerchantList) {
            if (StringUtils.isEmpty(merchantCode)) {
                LOGGER.error("== merchantCode为空 ==");
                return;
            }
            try {
                List<Integer> overdueDaysList = receiverDao.selectOverdueDaysListByMerchantCode(merchantCode);
                LOGGER.info("日期列表,merchantCode:{},{}", merchantCode, overdueDaysList.toString());
                for (Integer overdueDays : overdueDaysList) {
                    if (overdueDays > SystemConstants.MAX_COLLECTION_DAYS) {
                        continue;
                    }
                    LOGGER.info("按天分单开始,merchantCode:{},overdueDays:{}", merchantCode, overdueDays);
                    ClStageEntity clStageEntity = stageDao.getStagesByMerchantCodeAndOverdueDays(merchantCode, overdueDays);
                    if (clStageEntity == null) {
                        LOGGER.info("逾期天数对应的步骤为空,merchantCode:{},overdueDays:{}", merchantCode, overdueDays);
                        continue;
                    }

                    List<ClAgentsEntity> clAgentsEntityList = agentsDao.getActiveListByMerchantCode(merchantCode, clStageEntity.getStageCode());
                    if (CollectionUtils.isEmpty(clAgentsEntityList)) {
                        LOGGER.info("当前步骤的催收员为空,merchantCode:{},overdueDays:{},stageCode:{}", merchantCode, overdueDays, clStageEntity.getStageCode());
                        continue;
                    }
                    List<Integer> adminIdList = clAgentsEntityList.stream().map(ClAgentsEntity::getAdminId).collect(Collectors.toList());

                    //判断是否有待分配订单
                    List<ClLanguageOrderCountEntity> clLanguageOrderCountEntityList = receiverDao.selectLanguageOrderCountList(merchantCode, overdueDays);
                    if (CollectionUtils.isEmpty(clLanguageOrderCountEntityList)) {
                        continue;
                    }

                    //统计今日累计每种语言的分单数
                    List<ClLanguageOrderCountEntity> clCumulativeCountList = receiverDao.selectCumulativeLanguageOrderCountList(merchantCode, overdueDays, adminIdList);
                    if (CollectionUtils.isEmpty(clCumulativeCountList)) {
                        continue;
                    }

                    List<LanguageCount> languageCountList = clCumulativeCountList.stream().map(item -> {
                        LanguageCount languageCount = new LanguageCount()
                                .setUserLanguage(item.getUserLanguage())
                                .setCount(item.getCountNum());
                        return languageCount;
                    }).collect(Collectors.toList());

                    List<ClAgentsItem> clAgentsItemList = new ArrayList<>();
                    for (ClAgentsEntity clAgentsEntity : clAgentsEntityList) {
                        ClAgentsItem clAgentsItem = new ClAgentsItem();
                        BeanUtils.copyProperties(clAgentsEntity, clAgentsItem);

                        List<String> userLanguageList = new ArrayList<>();
                        JSONObject j1 = JSON.parseObject(clAgentsEntity.getExtraAttribute());
                        JSONArray j2 = j1.getJSONArray("languageWeight");
                        for (int i = 0; i < j2.size(); i++) {
                            String ss = j2.getJSONObject(i).getString("userLanguage");
                            userLanguageList.add(ss);
                        }
                        if (CollectionUtils.isEmpty(userLanguageList)) {
                            LOGGER.warn("代理人不会任何语言,忽略此代理,clAgentsEntity:{}", JSON.toJSONString(clAgentsEntity));
                            continue;
                        }

                        clAgentsItem.setUserLanguageList(userLanguageList);
                        clAgentsItemList.add(clAgentsItem);
                    }

                    //传入clAgentsItemList 将每个人每种语言的需求单数补全
                    this.dataMatching(languageCountList, clAgentsItemList);

                    List<ClDailyOrderCountEntity> clDailyOrderCountEntityList = allocationDetailDao.selectAgentDailyOrderCountList(merchantCode, today, overdueDays);
                    for (ClDailyOrderCountEntity clDailyOrderCountEntity : clDailyOrderCountEntityList) {
                        boolean result = setCurrentOrderCountNum(clDailyOrderCountEntity, clAgentsItemList);
                        if (!result) {
                            LOGGER.warn("当前语言设置当前值失败,clDailyOrderCountEntity:{},clAgentsItemList:{}", JSON.toJSONString(clDailyOrderCountEntity), JSON.toJSONString(clAgentsItemList));
                        }
                    }

                    //至此 基本数据计算完成 开始分单
                    //增加催收随机
                    LOGGER.info("调试日志1,clAgentsItemList:{}", JSON.toJSONString(clAgentsItemList));
                    Collections.shuffle(clAgentsItemList);
                    LOGGER.info("调试日志2,clAgentsItemList:{}", JSON.toJSONString(clAgentsItemList));

                    this.orderAllocationExec(clStageEntity, merchantCode, overdueDays, clAgentsItemList, today);

                    for (ClAgentsItem clAgentsItem : clAgentsItemList) {
                        LOGGER.info("分单展示日志,merchantCode:{},overdueDays:{},adminId:{},userLanguage:{},expectedCount:{},currentHoldingsCount:{},resultList:{}",
                                merchantCode, overdueDays,
                                clAgentsItem.getAdminId(), clAgentsItem.getUserLanguageList().toString(),
                                clAgentsItem.getExpectedCount(), clAgentsItem.getCurrentHoldingsCount(), JSON.toJSONString(clAgentsItem.getTheoreticalItemList()));
                    }
                }
                LOGGER.info("商户分单完成,merchantCode:{}", merchantCode);
            } catch (Exception e) {
                LOGGER.error("催收分单失败,merchantCode:{}", merchantCode, e);
            }
        }
    }

    private void orderAllocationExec(ClStageEntity clStageEntity, String merchantCode, Integer overdueDays, List<ClAgentsItem> clAgentsItemList, Date today) {
        for (ClAgentsItem clAgentsItem : clAgentsItemList) {
            for (TheoreticalItem theoreticalItem : clAgentsItem.getTheoreticalItemList()) {
                LOGGER.info("调试日志3,merchantCode:{},overdueDays:{},adminId:{},language:{}", merchantCode, overdueDays, clAgentsItem.getAdminId(), theoreticalItem.getUserLanguage());
                if (clAgentsItem.getMaxOrdersNum() != null && clAgentsItem.getCurrentHoldingsCount() >= clAgentsItem.getMaxOrdersNum()) {
                    LOGGER.info("代理人当前任务数饱和,merchantCode:{},overdueDays:{},language:{},clAgentsItem:{}",
                            merchantCode, overdueDays, theoreticalItem.getUserLanguage(), JSON.toJSONString(clAgentsItem));
                    continue;
                }

                Double needNum = theoreticalItem.getExpectedCount() - theoreticalItem.getCurrentHoldingsCount();

                //需求订单数四舍五入
                int value = new BigDecimal(needNum).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
                if (value <= 0) {
                    LOGGER.info("代理人当前语言已满,merchantCode:{},overdueDays:{},language:{},clAgentsItem:{}",
                            merchantCode, overdueDays, theoreticalItem.getUserLanguage(), JSON.toJSONString(clAgentsItem));
                    continue;
                }
                String userLanguage = theoreticalItem.getUserLanguage();

                List<ClReceiverEntity> clReceiverEntityList = receiverDao.selectReceiverListByMerchantCodeAndOverdueDays(merchantCode, overdueDays, userLanguage, ReceiverStatusEnum.INIT.getStatus(), new Page(value));
                LOGGER.info("调试日志4,merchantCode:{},overdueDays:{},adminId:{},language:{},size:{}", merchantCode, overdueDays, clAgentsItem.getAdminId(), theoreticalItem.getUserLanguage(), clReceiverEntityList.size());
                String batchNo = "";
                //插入批次记录
                ClAllocationBatchEntity clAllocationBatchEntity = null;
                if (CollectionUtils.isNotEmpty(clReceiverEntityList)) {
                    //创建一个批次号
                    batchNo = SerialNumberUtil.getBizOrderNumber(AdminNumberRuleEnum.BATCH);

                    //插入批次记录
                    clAllocationBatchEntity = new ClAllocationBatchEntity()
                            .setBatchNo(batchNo)
                            .setAdminId(clAgentsItem.getAdminId())
                            .setAgentName(clAgentsItem.getAdminName())
                            .setMerchantCode(merchantCode)
                            .setStageCode(clStageEntity.getStageCode())
                            .setCreateDt(new Date())
                            .setRepaymentDt(DateUtil.addDate(today, -1 * overdueDays))
                            .setStatus(AllocationBatchEnum.INIT.getStatus())
                            .setIsStart(1)
                            .setCreatedBy("system");
                    allocationBatchDao.insert(clAllocationBatchEntity);
                    LOGGER.info("创建批次成功,merchantCode:{},overdueDays:{},language:{},adminId:{},batchNo:{}", merchantCode, overdueDays, userLanguage, clAgentsItem.getAdminId(), batchNo);
                }

                for (ClReceiverEntity clReceiverEntity : clReceiverEntityList) {
                    if (clAgentsItem.getMaxOrdersNum() != null && clAgentsItem.getCurrentHoldingsCount() >= clAgentsItem.getMaxOrdersNum()) {
                        LOGGER.info("代理人当前任务数饱和222,merchantCode:{},overdueDays:{},language:{},clAgentsItem:{}",
                                merchantCode, overdueDays, theoreticalItem.getUserLanguage(), JSON.toJSONString(clAgentsItem));
                        break;
                    }

                    String allocationNo = SerialNumberUtil.getBizOrderNumber(AdminNumberRuleEnum.ALLOCATION);
                    ClAllocationDetailEntity clAllocationDetailEntity = new ClAllocationDetailEntity()
                            .setBatchNo(batchNo)
                            .setAllocationNo(allocationNo)
                            .setCollectionNo(clReceiverEntity.getCollectionNo())
                            .setStatus(AllocationStatusEnum.INIT.getStatus())
                            .setAdminId(clAgentsItem.getAdminId());

                    allocationDetailDao.insert(clAllocationDetailEntity);

                    clReceiverEntity.setStatus(ReceiverStatusEnum.ALLOCATE_SUCCESS.getStatus());
                    clReceiverEntity.setBatchNo(batchNo);
                    clReceiverEntity.setLastAgentId(clReceiverEntity.getCurrentAgentId());
                    clReceiverEntity.setCurrentAgentId(clAgentsItem.getAdminId());
                    receiverDao.updateByPrimaryKey(clReceiverEntity);

                    LOGGER.info("催收单分配成功,batchNo:{},collectionNo:{},allocationNo:{}", batchNo, clReceiverEntity.getCollectionNo(), allocationNo);

                    theoreticalItem.setCurrentHoldingsCount(theoreticalItem.getCurrentHoldingsCount() + 1);
                    clAgentsItem.setCurrentHoldingsCount(clAgentsItem.getCurrentHoldingsCount() + 1);
                }

                if (CollectionUtils.isNotEmpty(clReceiverEntityList)) {
                    //激活批次
                    clAllocationBatchEntity.setStatus(AllocationBatchEnum.SUCCESS.getStatus());
                    allocationBatchDao.updateByPrimaryKey(clAllocationBatchEntity);
                }
            }
        }

        //收尾操作 检测剩余的未分配的订单 随机分
        List<ClReceiverEntity> clReceiverEntityList = receiverDao.selectReceiverListByMerchantCodeAndOverdueDays(merchantCode, overdueDays, null, ReceiverStatusEnum.INIT.getStatus(), new Page(1000));
        if (CollectionUtils.isNotEmpty(clReceiverEntityList)) {
            for (ClReceiverEntity clReceiverEntity : clReceiverEntityList) {
                List<ClAgentsItem> clAgentsItemList1 = getCurLanguageAgents(clReceiverEntity.getUserLanguage(), clAgentsItemList);
                for (int i = 0; i < clAgentsItemList1.size(); i++) {
                    ClAgentsItem item = clAgentsItemList1.get(i);
                    if (item.getMaxOrdersNum() != null && item.getCurrentHoldingsCount() >= item.getMaxOrdersNum()) {
                        LOGGER.info("收尾操作,代理最大订单数已满,merchantCode:{},overdueDays:{},language:{},item:{}", merchantCode, overdueDays, clReceiverEntity.getUserLanguage(), JSON.toJSONString(item));
                        clAgentsItemList1.remove(i);
                        i--;
                    }
                }
                if (CollectionUtils.isEmpty(clAgentsItemList1)) {
                    continue;
                }

                int index = (int) (Math.random() * clAgentsItemList1.size());
                ClAgentsItem clAgentsItem = clAgentsItemList1.get(index);

                TheoreticalItem theoreticalItem = null;
                for (TheoreticalItem item : clAgentsItem.getTheoreticalItemList()) {
                    if (clReceiverEntity.getUserLanguage().equals(item.getUserLanguage())) {
                        theoreticalItem = item;
                        break;
                    }
                }
                if (theoreticalItem == null) {
                    continue;//理论上不可能到这里
                }

                //代码块
                {
                    //插入批次记录
                    //创建一个批次号
                    String batchNo = SerialNumberUtil.getBizOrderNumber(AdminNumberRuleEnum.BATCH);
                    //插入批次记录
                    ClAllocationBatchEntity clAllocationBatchEntity = new ClAllocationBatchEntity()
                            .setBatchNo(batchNo)
                            .setAdminId(clAgentsItem.getAdminId())
                            .setAgentName(clAgentsItem.getAdminName())
                            .setMerchantCode(merchantCode)
                            .setStageCode(clStageEntity.getStageCode())
                            .setCreateDt(new Date())
                            .setRepaymentDt(DateUtil.addDate(today, -1 * overdueDays))
                            .setStatus(AllocationBatchEnum.INIT.getStatus())
                            .setIsStart(1)
                            .setCreatedBy("system");
                    allocationBatchDao.insert(clAllocationBatchEntity);
                    LOGGER.info("收尾创建批次成功,merchantCode:{},overdueDays:{},language:{},adminId:{},batchNo:{}", merchantCode, overdueDays, clReceiverEntity.getUserLanguage(), clAgentsItem.getAdminId(), batchNo);


                    String allocationNo = SerialNumberUtil.getBizOrderNumber(AdminNumberRuleEnum.ALLOCATION);
                    ClAllocationDetailEntity clAllocationDetailEntity = new ClAllocationDetailEntity()
                            .setBatchNo(batchNo)
                            .setAllocationNo(allocationNo)
                            .setCollectionNo(clReceiverEntity.getCollectionNo())
                            .setStatus(AllocationStatusEnum.INIT.getStatus())
                            .setAdminId(clAgentsItem.getAdminId());

                    allocationDetailDao.insert(clAllocationDetailEntity);

                    clReceiverEntity.setStatus(ReceiverStatusEnum.ALLOCATE_SUCCESS.getStatus());
                    clReceiverEntity.setBatchNo(batchNo);
                    clReceiverEntity.setLastAgentId(clReceiverEntity.getCurrentAgentId());
                    clReceiverEntity.setCurrentAgentId(clAgentsItem.getAdminId());
                    receiverDao.updateByPrimaryKey(clReceiverEntity);

                    LOGGER.info("收尾催收单分配成功,batchNo:{},allocationNo:{}", batchNo, allocationNo);

                    theoreticalItem.setCurrentHoldingsCount(theoreticalItem.getCurrentHoldingsCount() + 1);
                    clAgentsItem.setCurrentHoldingsCount(clAgentsItem.getCurrentHoldingsCount() + 1);


                    //激活批次
                    clAllocationBatchEntity.setStatus(AllocationBatchEnum.SUCCESS.getStatus());
                    allocationBatchDao.updateByPrimaryKey(clAllocationBatchEntity);
                }
            }
        }

        LOGGER.info("催收当前日期分单成功,merchantCode:{},overdueDays:{},clAgentsItemList:{}", merchantCode, overdueDays, JSON.toJSONString(clAgentsItemList));
    }

    /**
     * 设置当天、当前代理、当前语言 已分配的订单数
     *
     * @param clDailyOrderCountEntity
     * @param clAgentsItemList
     */
    public boolean setCurrentOrderCountNum(ClDailyOrderCountEntity clDailyOrderCountEntity, List<ClAgentsItem> clAgentsItemList) {
        for (ClAgentsItem clAgentsItem : clAgentsItemList) {
            if (clDailyOrderCountEntity.getAdminId().equals(clAgentsItem.getAdminId())) {
                List<TheoreticalItem> theoreticalItemList = clAgentsItem.getTheoreticalItemList();
                for (TheoreticalItem theoreticalItem : theoreticalItemList) {
                    if (clDailyOrderCountEntity.getUserLanguage().equals(theoreticalItem.getUserLanguage())) {
                        //设置当前语言的已分配订单数
                        theoreticalItem.setCurrentHoldingsCount(clDailyOrderCountEntity.getNum().doubleValue());
                        //设置累计总数
                        clAgentsItem.setCurrentHoldingsCount(clAgentsItem.getCurrentHoldingsCount() + theoreticalItem.getCurrentHoldingsCount());
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 传入语言和用户 返回每个用于每种语言的需求数
     * 通过理论值的倒数计算权重 以期望达到方差值相对更小
     *
     * @param languageCounts
     * @param clAgentsItemList
     */
    public void dataMatching(List<LanguageCount> languageCounts, List<ClAgentsItem> clAgentsItemList) {
        for (int i = 0; i < clAgentsItemList.size(); i++) {
            ClAgentsItem clAgentsItem = clAgentsItemList.get(i);
            if (CollectionUtils.isEmpty(clAgentsItem.getUserLanguageList())) {
                clAgentsItemList.remove(i);
                i--;
            }
        }

        for (int i = 0; i < languageCounts.size(); i++) {
            LanguageCount languageCount = languageCounts.get(i);
            if (!this.getAvgCount(languageCount, clAgentsItemList)) {
                languageCounts.remove(i);
                i--;
            }
        }

        Map<String, LanguageCount> languageCountMap = EntityUtils.makeEntityMap(languageCounts, "userLanguage");

        for (ClAgentsItem clAgentsItem : clAgentsItemList) {
            for (String userLanguage : clAgentsItem.getUserLanguageList()) {
                LanguageCount lc = languageCountMap.get(userLanguage);
                Double avgCount = lc == null ? 0d : lc.getAvgCount();

                if (CollectionUtils.isEmpty(clAgentsItem.getTheoreticalItemList())) {
                    clAgentsItem.setTheoreticalItemList(new ArrayList<>());
                }
                if (clAgentsItem.getTheoreticalCount() == null) {
                    clAgentsItem.setTheoreticalCount(0d);
                }
                TheoreticalItem theoreticalItem = new TheoreticalItem(userLanguage, avgCount);
                clAgentsItem.getTheoreticalItemList().add(theoreticalItem);
                clAgentsItem.setTheoreticalCount(clAgentsItem.getTheoreticalCount() + avgCount);
            }
        }

        for (LanguageCount languageCount : languageCounts) {
            List<ClAgentsItem> curLanguageAgents = getCurLanguageAgents(languageCount.getUserLanguage(), clAgentsItemList);
            Double sumWeight = curLanguageAgents.stream().mapToDouble(item ->
                    (item.getTheoreticalCount() == null || item.getTheoreticalCount() == 0) ? 0 : (1d / item.getTheoreticalCount()))
                    .sum();
            for (ClAgentsItem clAgentsItem : curLanguageAgents) {
                for (TheoreticalItem theoreticalItem : clAgentsItem.getTheoreticalItemList()) {
                    if (languageCount.getUserLanguage().equals(theoreticalItem.getUserLanguage())) {
                        //期望值 = 倒数值 / 倒数求和值 * 当前语言的订单数
                        theoreticalItem.setExpectedCount(clAgentsItem.getTheoreticalCount() == null || clAgentsItem.getTheoreticalCount() == 0 ?
                                0 : ((1d / clAgentsItem.getTheoreticalCount()) / sumWeight * languageCount.getCount()));
                        clAgentsItem.setExpectedCount(clAgentsItem.getExpectedCount() + theoreticalItem.getExpectedCount());
                        break;
                    }
                }
            }
        }
    }

    private List<ClAgentsItem> getCurLanguageAgents(String userLanguage, List<ClAgentsItem> clAgentsItemList) {
        List<ClAgentsItem> list = new ArrayList<>();
        for (ClAgentsItem clAgentsItem : clAgentsItemList) {
            if (clAgentsItem.getUserLanguageList().contains(userLanguage)) {
                list.add(clAgentsItem);
            }
        }
        return list;
    }

    private boolean getAvgCount(LanguageCount languageCount, List<ClAgentsItem> clAgentsItemList) {
        int num = 0;
        for (ClAgentsItem clAgentsItem : clAgentsItemList) {
            if (clAgentsItem.getUserLanguageList().contains(languageCount.getUserLanguage())) {
                num++;
            }
        }
        if (num == 0) {
            return false;
        }
        languageCount.setAgentNum(num);
        languageCount.setAvgCount(languageCount.getCount().doubleValue() / num);
        return true;
    }


    public static class LanguageCount {
        private String userLanguage;

        /**
         * 待分配订单数
         */
        private Integer count = 0;

        /**
         * 催收员人数
         */
        private Integer agentNum = 0;

        /**
         * 平均值
         */
        private Double avgCount = 0d;

        public String getUserLanguage() {
            return userLanguage;
        }

        public LanguageCount setUserLanguage(String userLanguage) {
            this.userLanguage = userLanguage;
            return this;
        }

        public Integer getCount() {
            return count;
        }

        public LanguageCount setCount(Integer count) {
            this.count = count;
            return this;
        }

        public Integer getAgentNum() {
            return agentNum;
        }

        public LanguageCount setAgentNum(Integer agentNum) {
            this.agentNum = agentNum;
            return this;
        }

        public Double getAvgCount() {
            return avgCount;
        }

        public LanguageCount setAvgCount(Double avgCount) {
            this.avgCount = avgCount;
            return this;
        }
    }

    public static class ClAgentsItem extends ClAgentsEntity {
        private List<String> userLanguageList;

        private List<TheoreticalItem> theoreticalItemList;

        /**
         * 每门语言都按均分处理后 累计订单数
         */
        private Double theoreticalCount = 0d;

        /**
         * 总期望订单数
         */
        private Double expectedCount = 0d;

        /**
         * 当前语言 已分配的订单数 总数
         */
        private Double currentHoldingsCount = 0d;

        public List<String> getUserLanguageList() {
            return userLanguageList;
        }

        public ClAgentsItem setUserLanguageList(List<String> userLanguageList) {
            this.userLanguageList = userLanguageList;
            return this;
        }

        public List<TheoreticalItem> getTheoreticalItemList() {
            return theoreticalItemList;
        }

        public ClAgentsItem setTheoreticalItemList(List<TheoreticalItem> theoreticalItemList) {
            this.theoreticalItemList = theoreticalItemList;
            return this;
        }

        public Double getTheoreticalCount() {
            return theoreticalCount;
        }

        public ClAgentsItem setTheoreticalCount(Double theoreticalCount) {
            this.theoreticalCount = theoreticalCount;
            return this;
        }

        public Double getExpectedCount() {
            return expectedCount;
        }

        public ClAgentsItem setExpectedCount(Double expectedCount) {
            this.expectedCount = expectedCount;
            return this;
        }

        public Double getCurrentHoldingsCount() {
            return currentHoldingsCount;
        }

        public ClAgentsItem setCurrentHoldingsCount(Double currentHoldingsCount) {
            this.currentHoldingsCount = currentHoldingsCount;
            return this;
        }
    }

    public static class TheoreticalItem {
        private String userLanguage;

        /**
         * 每门语言都按均分处理后 当前语言的订单数
         */
        private Double count = 0d;

        /**
         * 期望订单数
         */
        private Double expectedCount = 0d;

        /**
         * 当前语言 已分配的订单数
         */
        private Double currentHoldingsCount = 0d;

        public TheoreticalItem() {
        }

        public TheoreticalItem(String userLanguage, Double count) {
            this.userLanguage = userLanguage;
            this.count = count;
        }

        public String getUserLanguage() {
            return userLanguage;
        }

        public TheoreticalItem setUserLanguage(String userLanguage) {
            this.userLanguage = userLanguage;
            return this;
        }

        public Double getCount() {
            return count;
        }

        public TheoreticalItem setCount(Double count) {
            this.count = count;
            return this;
        }

        public Double getExpectedCount() {
            return expectedCount;
        }

        public TheoreticalItem setExpectedCount(Double expectedCount) {
            this.expectedCount = expectedCount;
            return this;
        }

        public Double getCurrentHoldingsCount() {
            return currentHoldingsCount;
        }

        public TheoreticalItem setCurrentHoldingsCount(Double currentHoldingsCount) {
            this.currentHoldingsCount = currentHoldingsCount;
            return this;
        }
    }

}
